BestCoder Round 92

好久没写题解了。。感觉还是经常写点文字。题解勉强算是吧(雾

1001 Skip the Class

直接对于每种课程维护最大值和次大值即可

1002 Count the Sheep

题意:
$给定N\le 10^5个男羊,M\le 10^5个女羊,K\le 10^5个朋友关系$
$问满足A-B、B-C、C-D是朋友关系且A、B、C、D各不相同的,A-B-C-D这样序列的方案数$

分析:
$直接枚举B-C边,然后统计下两边的度就好了,别忘了减去自己$

1003 Girls Love 233

题意:
$给定长度N\le 100的由字符’2’和’3’构成的字符串$
$有\lfloor{M\over 2}\rfloor次操作次数,每次可以交换2个相邻的字符$
$最多能使这个字符串中有多少个子串”233”呢$

分析:
$官方题解给了一个很妙的dp$
$可以发现答案其实只跟’2’有关,即’3’和’3’换是毫无意义的$
$于是我们可以抠出来所有’2’的位置,那么只对’2’有交换的花费$
$接下来考虑dp,f[i][j][k][3]:=$
$选取了i个’2’,j个’3’,使用了k次交换,状态是s的最多子串数$
$s状态显然有i\in [0,3):’2’后面有i个’3’,如果有第一次有2个’3’显然答案+1$
$最终ans=\displaystyle\max_{k, s}\{ f[c2][c3][k][s]\}$

代码:

//
//  Created by TaoSama on 2017-02-27
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cerr << #x << " = " << x << "  "
#define prln(x) cerr << #x << " = " << x << endl
const int N = 1e2 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m;
char s[N];
int f[N][N][N / 2][3];

void getMax(int& x, int y) {
    if(x < y) x = y;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        vector<int> p(1, 0);
        scanf("%d%d%s", &n, &m, s + 1);  m >>= 1;
        for(int i = 1; i <= n; ++i) if(s[i] == '2') p.push_back(i);
        int c2 = p.size() - 1, c3 = n - c2;

        for(int i = 0; i <= c2; ++i)
            for(int j = 0; j <= c3; ++j)
                for(int k = 0; k <= m; ++k)
                    for(int z = 0; z < 3; ++z)
                        f[i][j][k][z] = -INF;
        f[0][0][0][2] = 0;
        for(int i = 0; i <= c2; ++i) {
            for(int j = 0; j <= c3; ++j) {
                for(int k = 0; k <= m; ++k) {
                    for(int z = 0; z < 3; ++z) {
                        if(f[i][j][k][z] < 0) continue;
                        if(i < c2) {
                            int nk = k + abs(i + j + 1 - p[i + 1]);
                            if(nk <= m) getMax(f[i + 1][j][nk][0], f[i][j][k][z]);
                        }
                        if(j < c3) {
                            int nz = z, one = 0;
                            if(nz != 2) {
                                if(++nz == 2) ++one;
                            }
                            getMax(f[i][j + 1][k][nz], f[i][j][k][z] + one);
                        }
                    }
                }
            }
        }

        int ans = 0;
        for(int k = 0; k <= m; ++k)
            for(int z = 0; z < 3; ++z)
                getMax(ans, f[c2][c3][k][z]);
        printf("%d\n", ans);
    }

    return 0;
}

1004 Game Arrangement

题意:
$给定N\le 10^4段空闲时间[L_i, R_i],1\le L_i\le R_i\le 10^9$
$给定M\le 10^4个游戏的感兴趣时段[l_i, r_i],1\le l_i\le r_i\le 10^9,并且需要持续时间1\le d_i\le 10^9$
$对于i类游戏只能全在它的感兴趣时段,以及某段空闲时段才可以玩$
$问最多能玩游戏的次数$

分析:
$如果数据范围不是10^9的话,显然可以按时间来dp,就不多说了$
$可以考虑贪心,因为物品的价值都是1,就可以贪心了$
$对于当前时间,对某个游戏肯定选择持续短的,就可以用堆或者set来贪心了$
$首先把空闲时间和游戏按照起始时间排序$
$考虑枚举每一段空闲时间,首先把当前能玩的游戏全部加进去堆里$
$再把一把都玩不了的都删了,然后取出第一个能玩的$
$当前这个游戏能玩多久由三个东西限制,一个是空闲时间,一个是感兴趣时间$
$还有下一个游戏的开始时间来限制$
$肯定尝试下取整个是一定可以的$
$然后你以为就对了??显然不是,我还可以后延续到下一个游戏的时间$
$所以就考虑再玩一个,但是只是尝试,所以拆成两半,一个是现在玩一些$
$剩下的时间成为一个新的游戏塞进去,之后直接尝试下一个游戏就可以了$
$这样贪心就对了$
$注意一下边界的细节,时间复杂度是O(nlogn)$
$感谢bc大佬的代码$

//
//  Created by TaoSama on 2017-02-27
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cerr << #x << " = " << x << "  "
#define prln(x) cerr << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m;
struct Node {
    int l, r, d;
    bool operator<(const Node& r) const {
        return d > r.d;
    }
    void see() {
        pr(l); pr(r); prln(d);
    }
};

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        vector<Node> a, b;
        a.reserve(n); b.reserve(m);
        for(int i = 1; i <= n; ++i) {
            int l, r; scanf("%d%d", &l, &r);
            if(a.size() && a.back().r + 1 == l)
                a.back().r = r;
            else a.push_back({l, r, 0});
        }
        for(int i = 1; i <= m; ++i) {
            int l, r, d; scanf("%d%d%d", &l, &r, &d);
            b.push_back({l, r, d});
        }
        b.push_back({INF, -1, -1});
        sort(b.begin(), b.end(), [](const Node & a, const Node & b) {
            return a.l < b.l;
        });

        int ans = 0;
        priority_queue<Node> q;
        for(int i = 0, j = 0; i < a.size(); ++i) {
            int cur = a[i].l;
            for(; cur <= a[i].r;) {
                for(; b[j].l <= cur; ++j) q.push(b[j]); //insert
                while(q.size() && q.top().r - q.top().d + 1 < cur) q.pop(); //delete
                if(!q.size()) {cur = b[j].l; continue;}

                Node tp = q.top();
                int r = min(a[i].r, tp.r);
                int cnt = (min(r, b[j].l - 1) - cur + 1) / tp.d;
                ans += cnt;
                cur += cnt * tp.d;
                if(!cnt) {
                    int nxt = cur + tp.d - 1;
                    if(nxt <= r) q.push({b[j].l, nxt, nxt - b[j].l + 1});
                    cur = b[j].l;
                }
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}